<template>
  <div class="xhykTreeV2" ref="xhykTreeDom">
    <el-tree-v2 ref="treeV2Dom" class="treeV2X" :props="dataProps"
                :filter-method="filterFn" @node-click="nodeClickFn"
                :current-node-key="nowNodeKey" :data="dataList"
                :always="scrollAlways"
                :highlight-current="!props.noHighlight" v-bind="$attrs">
      <template #default="{node,data}">
        <slot :node="node" :data="data"/>
      </template>
    </el-tree-v2>
  </div>
</template>

<script setup>
import {ref, computed, watch, onMounted,onUnmounted,nextTick} from 'vue'
import {ElTreeV2} from 'element-plus'


const props = defineProps({

  modelValue: {
    type: Object,
    default: {}
  },

  // 不进行高亮显示
  noHighlight: {
    type: Boolean,
    default: false
  },

  // region 数据结构

  // props的label字段，如果props值不为undefined则无效
  labelField: {
    type: String,
    default: 'name'
  },

  // props的value字段，如果props值不为undefined则无效
  valueKey: {
    type: String,
    default: 'id'
  },

  // props的disabled字段，如果props值不为undefined则无效
  disabledField: {
    type: String,
    default: 'disabled'
  },

  props: {
    type: Object,
    default: undefined
  },
  // endregion


  // region 过滤文本
  search: {
    type: String,
    default: ""
  },

  // 过滤方法。默认是判断label是否包含
  filterFn: {
    type: Function,
    default: undefined
  },

  // endregion

  // region 数据处理
  data: {
    type: Object,
    default: []
  },

  // 不将数据转成一层父子级结构，如果处理数据慢则设置true：[ [父级,父级2,子级] , [] ]
  noLevel: {
    type: Boolean,
    default: false
  },
  // endregion

  // 滚动条设置。这个没有横向。跟el-table不同
  scrollStyle: {
    type: Object,
    default: {
      // always: true, // 可以显示滚动条时，总是显示滚动条
      // width: '14px', // 两条滚动条宽带。接受Number或string
      // widthX: '14px', // 横向滚动条宽带。下面值都有总体和XY区分（XY权限优先）
      // color: '#909399',  // 两条滚动条颜色
      // opacity: 0.7, // 滚动条透明度
      // translate: '2px', // 偏移量
      // show: true,// 不写或true为显示两条滚动条，否则隐藏
    }
  },
})

const emit = defineEmits(['update:modelValue', 'nodeClick'])


// region 工具类
class StringUtil {

  // 字符串或数组去重复元素
  removeSame(Obj) {
    if (typeof Obj === 'string') {
      const text = new Set(Obj)
      return [...text].join('')
    } else if (Obj instanceof Array) {
      return Array.from(new Set(Obj))
    } else {
      return Obj
    }
  }

  // region 取字符串中间
  /*
  *   str:被取字符串
  *   strBefore:前字符串
  *   strAfter:后字符串
  *   False:失败时返回的字符串
  *   return：返回string
  * */
  getStrCenter(str, strBefore, strAfter, False = undefined, toLower = false) {
    if (str == undefined) {
      return False
    }

    // 保留原有的
    const oldStr = str;
    if (toLower) {
      str = str.toLowerCase()
      strBefore = strBefore.toLowerCase()
      strAfter = strAfter.toLowerCase()
    }
    try {
      let before = str.indexOf(strBefore);
      let after = str.indexOf(strAfter, before + strBefore.length);
      if (before == -1 || after == -1) {
        return False
      } else {
        return oldStr.slice(before + strBefore.length, after)
      }
    } catch (err) {
      return False
    }
  }

  // endregion

  // region 取文本中间2。倒找（右边）strBefore文本，然后根据那位置左边开始找strAfter
  getStrCenterR(str, strBefore, strAfter, False = undefined, toLower = false) {
    if (str == undefined) {
      return False
    }
    const oldStr = str;
    if (toLower) {
      str = str.toLowerCase()
      strBefore = strBefore.toLowerCase()
      strAfter = strAfter.toLowerCase()
    }
    try {
      let before = str.lastIndexOf(strBefore);
      let after = str.indexOf(strAfter, before + strBefore.length);
      if (before == -1 || after == -1) {
        return False
      } else {
        return oldStr.slice(before + strBefore.length, after)
      }
    } catch (err) {
      return False
    }
  }

  // endregion

  // region 取文本中间3。倒找（右边）strBefore文本，然后根据那位置右边开始找strAfter
  getStrCenterRR(str, strBefore, strAfter, False = undefined, toLower = false) {
    if (str == undefined) {
      return False
    }
    const oldStr = str;
    if (toLower) {
      str = str.toLowerCase()
      strBefore = strBefore.toLowerCase()
      strAfter = strAfter.toLowerCase()
    }
    try {
      let before = str.lastIndexOf(strBefore);
      let after = str.lastIndexOf(strAfter)
      // 如果前面字符串在前面就返回False字符串
      if (before > after || after == -1 || before == -1) {
        return False
      } else {
        return oldStr.slice(before + strBefore.length, after)
      }
    } catch (err) {
      return False
    }
  }

  // endregion

  // region 取文本中间4。左边取before文本的右侧，右边取strAfter文本的左侧
  getStrCenterLR(str, strBefore, strAfter, False = undefined, toLower = false) {
    let rightStr = this.getStrRight(str, strBefore, False, toLower)
    let leftStr = this.getStrLeftR(rightStr, strAfter, False, toLower)
    return leftStr;
  }

  // endregion

  // region 取指定文本右边
  getStrRight(str, strBefore, False = undefined, toLower = false) {
    if (str == undefined) {
      return False
    }
    const oldStr = str;
    if (toLower) {
      str = str.toLowerCase()
      strBefore = strBefore.toLowerCase()
    }
    try {
      let before = str.indexOf(strBefore);
      if (before == -1) {
        return False
      }
      return oldStr.slice(before + strBefore.length)
    } catch (err) {
      return False
    }
  }

  // endregion

  // region 取指定文本左边
  getStrLeft(str, strAfter, False = undefined, toLower = false) {
    if (str == undefined) {
      return False
    }
    const oldStr = str;
    if (toLower) {
      str = str.toLowerCase()
      strAfter = strAfter.toLowerCase()
    }
    try {
      let after = str.indexOf(strAfter);
      if (after == -1) {
        return False
      }
      return oldStr.slice(0, after)
    } catch (err) {
      return False
    }
  }

  // endregion

  // region 倒数，取指定文本右边
  getStrRightR(str, strBefore, False = undefined, toLower = false) {
    if (str == undefined) {
      return False
    }
    const oldStr = str
    if (toLower) {
      str = str.toLowerCase()
      strBefore = strBefore.toLowerCase()
    }
    try {
      let before = str.lastIndexOf(strBefore);
      if (before == -1) {
        return False
      }
      return oldStr.slice(before + strBefore.length)
    } catch (err) {
      return False
    }
  }

  // endregion

  // region 倒数，取指定文本左边。strBefore：查找位置的文本
  getStrLeftR(str, strAfter, False = undefined, toLower = false) {
    if (str == undefined) {
      return False
    }
    const oldStr = str;
    if (toLower) {
      str = str.toLowerCase()
      strAfter = strAfter.toLowerCase()
    }
    try {
      let after = str.lastIndexOf(strAfter);
      if (after == -1) {
        return False
      }
      return oldStr.slice(0, after)
    } catch (err) {
      return False
    }
  }

  // endregion

  // region 删除前后指定文本。'wewe22we我0-we88we9we' -> '22we我0-we88we9'
  trimStr(str, delStr = '', toLower = false) {
    if (delStr.trim() == '') {
      return str.trim()
    }
    let flags = 'g'
    if (toLower) {
      flags = 'gi'
    }
    ;
    // 删除左边
    const regL = new RegExp(`^(` + delStr + ')*', flags)
    let tempStr = str.replace(regL, '')
    // 删除右边
    const regR = new RegExp('(' + delStr + ')*$', flags)
    tempStr = tempStr.replace(regR, '')
    return tempStr
  }

  // endregion

  // region 删除前面指定文本
  trimStrStart(str, delStr = '', toLower = false) {
    if (delStr.trim() == '') {
      return str.trim()
    }
    let flags = 'g'
    if (toLower) {
      flags = 'gi'
    }
    ;
    // 删除左边
    const regL = new RegExp(`^(` + delStr + ')*', flags)
    let tempStr = str.replace(regL, '')
    return tempStr
  }

  // endregion

  // region 删除后面指定文本
  trimStrEnd(str, delStr = '', toLower = false) {
    if (delStr.trim() == '') {
      return str.trim()
    }
    let flags = 'g'
    if (toLower) {
      flags = 'gi'
    }
    ;
    // 删除右边
    const regR = new RegExp('(' + delStr + ')*$', flags)
    let tempStr = str.replace(regR, '')
    return tempStr

  }

  // endregion

  // region 正则替换
  /*
  * regExp：正则文本，如：'\\d{1,2}'匹配数字，注意\\转义。
  * str：被替换的文本，如：'ab1234cd1234炫黑易酷9099'。
  * func：匿名函数(参数1)，参数1为当前匹配到的文本段，必须返回String，该String会替换成新文本段。
  * flags：匹配标志，默认为：'g'。
  * */
  replaceEx(regExp, str, func, flags = 'g') {
    const reg = new RegExp(regExp, flags)
    // 取得全部匹配结果，然后切断替换
    const resultList = str.match(reg)
    let result = ''
    let afterStr = str
    // 防止匹配不到
    resultList instanceof Array && resultList.find((value) => {
      // 取得索引,并分割提取前面部分
      const index = afterStr.indexOf(value)
      const beforeStr = afterStr.slice(0, index + value.length)
      // 替换出前面部分的value文本并保存到结果。根据匿名函数修改新文本，匿名函数参数为匹配到的旧文本
      result = result + beforeStr.replace(value, func(value))
      // 接下来直接判断后半旧文本
      afterStr = afterStr.slice(index + value.length)
    })
    // console.log(result)
    // 加上剩余的旧文本
    result = result + afterStr
    return result
  }

  // endregion

  // 文本1是否包含文本2。是否转为小写再比较
  includesStr(str1, str2, str1Lower = true, str2Lower = true) {
    let tempStr1 = String(str1)
    let tempStr2 = String(str2)
    if (str1Lower) {
      tempStr1 = tempStr1.toLowerCase()
    }
    if (str2Lower) {
      tempStr2 = tempStr2.toLowerCase()
    }
    return tempStr1.includes(tempStr2)
  }

  // 判断两个文本是否相对，是否转为小写再比较
  equalStr(str1, str2, str1Lower = true, str2Lower = true) {
    let tempStr1 = String(str1)
    let tempStr2 = String(str2)
    if (str1Lower) {
      tempStr1 = tempStr1.toLowerCase()
    }
    if (str2Lower) {
      tempStr2 = tempStr2.toLowerCase()
    }
    return tempStr1 == tempStr2
  }

  // 有值，非空也非undefined，而且不在黑名单里面
  haveText(text = undefined, blackList = []) {
    if (text != undefined && text.length > 0) {
      const haveBlack = blackList?.find(row => row == text)
      if (haveBlack) {
        return false
      }
      return true
    }
    return false
  }

  // 是空值。或者等于inList里面的值
  isNothing(text, inList = []) {
    if (text == undefined || text === null || text.length == 0) {
      return true
    }
    const haveIn = inList.find(row => row === text)
    if (haveIn) {
      return true
    }
    return false
  }

}

const stringUtil = new StringUtil()

class ListUtil {

  // 遍历嵌套取值。根据field字段，在data中通过对比value。找到则返回该对象。根据children字段遍历下级列表
  async getChildren(data = [], value = '', toLower = false, field = 'id', children = 'children') {
    if (toLower && typeof value == 'string') {
      value = value.toLowerCase()
    }
    const haveData = await this.findChildren(data, (child) => {
      let childValue = child[field]
      if (toLower && typeof childValue == 'string') {
        childValue = childValue.toLowerCase()
      }
      if (childValue == value) {
        return true
      }
    }, children)
    return haveData
  }

  // 字符串或数组去重复元素
  removeSame(Obj) {
    if (typeof Obj === 'string') {
      const text = new Set(Obj)
      return [...text].join('')
    } else if (Obj instanceof Array) {
      return Array.from(new Set(Obj))
    } else {
      return Obj
    }
  }

  // 遍历嵌套取值。根据children字段遍历下级列表。返回当前子级对象
  async findChildren(data = [], callBack = undefined, children = "children") {
    let tempResult = undefined
    const findFn = async (ChildData, father) => {
      let isOk = false
      for (let item of ChildData) {
        if (typeof callBack == "function") {
          const isBreak = await callBack(item, father)
          if (isBreak == true) {
            tempResult = item
            isOk = true
            break;
          }
        }
        if (isOk) {
          tempResult = item
          return item
        }

        // 如果有下一级数据
        if (item[children] != undefined && item[children].length > 0) {
          let result = await findFn(item[children], item)
          if (result) {
            return true
          }
        }
      }
      return isOk;
    }
    await findFn(data, {})
    return tempResult
  }

  // 找到最上层父级，并将关联的放到一个列表中。
  // childValue：undefined时为全部，否则为只获取指定子级。
  // callBack：(row：当前遍历到的对象,thisObj: 当前对比的父级)
  // toLower：row[key]的值是否进行到小后再对比。
  // fatherKey：用来保存父级key对应值的。childkey:标记是最后一层
  // 返回：[ [{id:'1},{id:'1-1'}], [{id:'1'},{id:'1-2'}] ]
  async getFather(data = [], childValue = undefined, callBack = undefined,
                  key = "id", toLower = true, childField = "children",
                  fatherKey = undefined, childKey = undefined) {
    if (data.length <= 0 || !(data instanceof Array)) {
      return []
    }
    let date = Date.now();
    let tempKey = `temp_father${date}`
    if (fatherKey != undefined) {
      tempKey = fatherKey
    }
    let tempChildKey = `temp_child${date}`
    if (childKey != undefined) {
      tempChildKey = childKey
    }
    // 改成一级列表，并重新赋值父级字段
    let tempList = this.unlockTree(data, key, childField, tempKey, false, tempChildKey)

    // 获取指定关联的方法
    async function _fn(newChildValue) {
      let tempResult = []
      // 每层的关联
      let secondList = []
      let tempObj = {[key]: newChildValue}
      while (true) {
        // 遍历获取判断对象
        let thisObj = undefined
        for (let i = 0; i < tempList.length; i++) {
          let row = tempList[i]
          let isOk = false
          // 自定义判断
          if (typeof callBack == 'function') {
            isOk = await callBack(row, tempObj)
          } else {
            isOk = stringUtil.equalStr(row[key], tempObj[key], toLower, toLower)
          }
          if (isOk) {
            thisObj = row
            // 如果是只获取指定值，那么移除已经获取到的数据，减少循环
            if (childValue != undefined) {
              tempList.splice(i, 1)
            }
            break;
          }
        }

        if (thisObj) {
          secondList.unshift(thisObj)
          // 获取它的父级id
          let fatherkey = thisObj[tempKey]
          if (fatherkey == undefined) {
            // 没有上一级了
            break;
          }
          tempObj[key] = fatherkey
        } else {
          break;
        }
      }
      tempResult.push(secondList)
      return tempResult
    }

    // 获取全部关联
    if (childValue == undefined) {
      // 先获取全部最后一级
      const childList = tempList.filter(item => item[tempChildKey] == true)
      // 开始处理子级数据
      let result = []
      for (let i = 0; i < childList.length; i++) {
        const tempResult = await _fn(childList[i][key])
        if (tempResult.length > 0) {
          result.push(tempResult[0])
        }
      }
      return result
    }
    // 获取指定关联
    else {
      return await _fn(childValue)
    }
  }

  // 创建树形结构数据。outList：提供要给列表变量参数，用来保存没有处理的数据
  async makeTreeAsync(dataList, outList = [], id = 'id', parent = {field: 'parentId', value: '-1'},
                      childField = 'children', sortField = 'sort', orderType = -1) {
    let result = []
    let childList = [] // 临时保存子级

    // 处理父级
    dataList.forEach(item => {
      if (item[parent.field] == parent.value || item[parent.field] == undefined) {
        if (item[childField] == undefined) {
          item[childField] = []
        }
        result.push(item)
      } else {
        childList.push(item)
      }
    })
    if (dataList.length > 0 && dataList[0][sortField] != undefined) {
      dataList.sort((v1, v2) => v1[sortField] > v2[sortField] ? orderType : -orderType)
    }

    // 处理子级
    // 记录上一次子级列表的剩余的数据条数
    let lastLen = childList.length
    // 找不到父级的列表数据
    let tempList = []

    while (1) {
      // 这次循环剩余的子级列表
      tempList = []
      for (let i = 0; i < childList.length; i++) {
        let item = childList[i]
        // 找到这个item菜单所属的父级对象
        let father = await this.findChildren(result, (childItem, fatherItem) => {
          // 此处的childItem可能是这个item的父级对象
          if (item[parent.field] == childItem[id]) {
            return true
          }
        }, childField)

        if (father) {
          // 如果是第一次赋值，新增一个children字段
          if (!father[childField]) {
            father[childField] = []
          }
          father[childField].push(item)
          // 排序
          if (father[childField].length > 0 && father[childField][0][sortField] != undefined) {
            father[childField].sort((v1, v2) => v1[sortField] > v2[sortField] ? orderType : -orderType)
          }
        } else {
          // 找不到这个item的父级，可能是子级顺序在这个父级之前
          tempList.push(item)
        }
      }

      if (lastLen == tempList.length) {
        // 没有弹出过，则抛出
        break
      } else {
        // 有弹出过，刷新数据条数记录，继续遍历剩余的子级
        childList = tempList
        lastLen = childList.length
      }
    }
    this.changeListByNum(outList, tempList)
    return result
  }

  // 根据数值创建数组
  makeList(min, max, value = undefined) {
    const tempList = Array.from({length: max - min + 1}, (_, index) => {
      return value == undefined ? min + index : value
    })
    return tempList
  }

  // 重置为一级结构。
// fatherField：自定义父级字段名（父级对象的参数id值，第一层为undefined）。
// removeChild:是否删除children列表。
// isLastChild：自定义最后一级字段名(Boolean值)
  unlockTree(dataList, id = 'id', childField = 'children',
             fatherField = undefined, removeChild = true, isLastChild = undefined) {
    let result = []

    function _fn(childrenList, fatherId) {
      childrenList.forEach(item => {
        if (item[childField] != undefined && item[childField] instanceof Array) {
          const temp = item[childField]
          if (temp.length <= 0 && isLastChild != undefined) {
            item[isLastChild] = true
          }
          if (removeChild) {
            delete item[childField]
          }
          _fn(temp, item[id])
        } else {
          item[isLastChild] = true
        }

        // 存在自定义父级字段名称
        if (fatherField != undefined) {
          item[fatherField] = fatherId
        }
        result.push(item)
      })
    }

    _fn(dataList, undefined)
    return result;
  }

  // 给树形结构添加表示第几层的字段。levelField：表示层数的字段名
  treeLevel(treeObj, childField = 'children', levelField = 'level') {
    function _add(item, nowLevel) {
      item.forEach(child => {
        let level = nowLevel
        child[levelField] = nowLevel
        if (child[childField] instanceof Array && child[childField].length > 0) {
          _add(child[childField], level + 1)
        }
      })
    }

    _add(treeObj, 0)
  }

  // 将一维变成为二维数组。number：每个包含三个元素
  makeArray(number = 3, ListObj = []) {
    // let level = list[0]
    // let number = list[1]
    let result = []
    // 根据元素数量判断可以生成多少个维数数组
    // let listLength = Math.floor(ListObj.length / number)
    // if ((ListObj.length % number) > 0) {
    //   listLength += 1
    // }
    let outNumber = 0
    while (ListObj.length > outNumber) {
      // 提取元素个数
      const tempList = ListObj.slice(outNumber, outNumber + number)
      result.push(tempList)
      outNumber += number
    }
    return result
  }

  // 判断两个数组的元素都相等，忽视索引位置
  isEqualArray(arr1, arr2) {
    if (arr1.length !== arr2.length) {
      return false;
    }
    const sortedArr1 = arr1.slice().sort(); // 对数组进行排序
    const sortedArr2 = arr2.slice().sort();
    return sortedArr1.every((item, index) => item === sortedArr2[index]);
  }

  // 判断第一个数组包含了第二个数组，忽视索引值。第二数组空会返回true
  isArrayIncludes(arr1, arr2) {
    if (arr1.length < arr2.length) {
      return false;
    }
    return arr2.every(item => arr1.includes(item));
  }

  // 静态，保存原对象内存地址不变，更新指定数量的成员
  changeListByNum(oldList = [], newData = [], number = -1, startIndex = 0) {
    if (number = -1) {
      // 删除全部
      oldList.splice(0, oldList.length)
    } else {
      // 删除指定数
      oldList.splice(startIndex, number)
    }
    oldList.push(...newData)
  }

  // 判断列表里面是否包含对象。字符串时是否转为小写再比较
  includesList(list, obj, toLower = true) {
    let result = false
    list.find(row => {
      let tempObj = obj
      let value = row;
      if (toLower) {
        if (typeof tempObj == 'string') {
          tempObj = obj.toLowerCase()
        }
        if (typeof value == 'string') {
          value = row.toLowerCase()
        }
      }
      if (value == tempObj) {
        result = true
        return true
      }
    })
    return result
  }

  // 根据sku值生成列表。arr：[['a', 'b', 'c'], ['A',], ['1', '2']]。
  // splitStr：分割父，如果为undefined则不进行拼接处理，此时返回的是List<List<object>>
  async skuList(arr = [], splitStr = undefined) {
    let result = []
    if (!arr || arr.length <= 0) {
      return result
    }
    const generateSkuList = async (level, sku) => {
      if (level === arr.length) {
        if (splitStr == undefined) {
          result.push(sku);
        } else {
          result.push(sku.join(splitStr));
        }
        return;
      }

      for (let i = 0; i < arr[level].length; i++) {
        let newSku = [...sku]; // 复制一份 sku 数组，避免修改原数组
        newSku.push(arr[level][i]);
        await generateSkuList(level + 1, newSku);
      }
    }

    await generateSkuList(0, []);


    // 结果为：["a;A;1","a;A;2",...]
    return result;
  }

  // 融合数值区间：[[1,3],[2,5],[7,10]] => [[1,5],[7,10]]
  numberAssign(intervals) {
    // Step 1: 按照起始位置排序
    intervals.sort((a, b) => a[0] - b[0]);

    // Step 2: 创建结果列表
    let merged = [];

    // Step 3: 遍历排序后的原始列表
    for (let interval of intervals) {
      // Step 4: 判断是否相交
      if (!merged.length || interval[0] > merged[merged.length - 1][1]) {
        merged.push(interval);
      } else {
        // Step 5: 更新最后一个区间的结束位置
        merged[merged.length - 1][1] = Math.max(merged[merged.length - 1][1], interval[1]);
      }
    }

    return merged;
  }

  // 取列表左边。include：false不包含自己
  async getListLeft(list = [], callBack = undefined, include = false) {
    // 不用findIndex,findIndex在callBack的async-await会处理异常
    let thisIndex = -1
    for (let i = 0; i < list.length; i++) {
      const row = list[i]
      if (typeof callBack == 'function') {
        const callResult = await callBack(row)
        if (callResult) {
          thisIndex = i
          break
        }
      }
    }
    if (include) {
      thisIndex += 1
    }
    return list.slice(0, thisIndex)
  }

}

const thisListUtil = new ListUtil()

// 防抖/节流类
class MyTimeOut {

  // oneTime：节流延迟，只有第一次有效，常用滚动分页。lastTime：防抖延迟，只有最后一次有效，常用搜索框
  constructor(oneTime = 500, lastTime = 500) {
    this.oneTime = oneTime
    this.lastTime = lastTime

    this.oneId = undefined
    this.lastId = undefined
  }

  // 节流执行。只执行第一次
  runOne(callBack) {
    if (this.oneId) {
      return;
    }
    this.oneId = setTimeout(async () => {
      await callBack()
      this.oneId = undefined
    }, this.oneTime)
  }

  // 防抖执行。只执行最后一次
  runLast(callBack) {
    clearTimeout(this.lastId)
    this.lastId = setTimeout(async () => {
      await callBack()
      this.lastId = undefined
    }, this.lastTime)
  }

  // 死循环等待，直到breakCallBack为true
  async runSwitch(callBack, breakCallBack) {
    while (true) {
      const result = await breakCallBack()
      if (result) {
        break
      }
    }
    callBack()
  }
}

// 判断prop传参的值。接受[String,Number]的style样式尺寸
function getPx(text, defaultPx = 'px') {
  let tempList = ['px', 'rpx', 'vw', 'vh', '%', 'em', 'auto']
  if (typeof text == 'string') {
    const havePx = tempList.find(row => text.includes(row))
    if (havePx) {
      return text
    } else {
      return `${parseFloat(text)}${defaultPx}`
    }
  } else if (typeof text == 'number') {
    let temp = parseFloat(text)
    return `${temp}${defaultPx}`
  }
  return text
}

// endregion


const treeV2Dom = ref({})


// 数据结构
const dataProps = computed(() => {
  let result = {
    value: props.valueKey,
    label: props.labelField,
    children: 'children',
    disabled: props.disabledField
  }
  return result
})

// region 获取父元素的宽高
const xhykTreeDom = ref({})

const fatherW = ref(0)
const fatherH = ref(0)

const getHW = () => {
  nextTick(() => {
    const style = window.getComputedStyle(xhykTreeDom.value)
    if (style.width) {
      fatherW.value = `${parseFloat(style.width)}px`
    }
    if (style.height) {
      fatherH.value = parseFloat(style.height)
    }
  })
}
// endregion

// region 处理数据
// 树形结构的数据
const dataList = ref([])

// 转为一层结构的父子层数据
const levelList = ref([])

// 转为没有关联的一层数据
const allList = ref([])

const myTimeOut = new MyTimeOut(200, 200)
watch(() => props.data, async (newV, oldV) => {
  myTimeOut.runLast(async () => {
    dataList.value = newV || []
    allList.value = []
    const tempList = JSON.parse(JSON.stringify(dataList.value || []))

    thisListUtil.findChildren(tempList, (child) => {
      const temp = JSON.parse(JSON.stringify(child))
      delete temp[dataProps.value.children]
      allList.value.push(temp)
    }, dataProps.value.children)

    if (props.noLevel != true) {
      levelList.value = await thisListUtil.getFather(tempList, undefined,
          undefined, dataProps.value.value, false, dataProps.value.children)


    } else {
      levelList.value = []
    }
  })
}, {immediate: true, deep: true})

// 通过levelList去查找层级关系：[ [{id:'1},{id:'1-1'}], [{id:'1'},{id:'1-2'}] ]
const findTreeLevelFn = async (keyValue) => {
  const thisLevel = levelList.value?.find(oneItem => {
    const thisOne = oneItem.find(row => {
      if (row[dataProps.value.value] == keyValue) {
        return true
      }
    })
    if (thisOne) {
      return true
    }
  })

  // 没有levelList数据或者找不到
  if (thisLevel == undefined) {
    return []
  }

  // 只获取当前keyValue前面和自己的上级，下级去掉
  const newList = await thisListUtil.getListLeft(thisLevel, (row) => {
    if (row[dataProps.value.value] == keyValue) {
      return true
    }
  })
  return newList
}

// endregion


// region 组件方法

// 展开/隐藏全部节点。获取指定节点
const expandNode = async (show = false, nodeOrNodeKey = undefined) => {
  if (nodeOrNodeKey == undefined) {
    // 展开/隐藏全部
    allList.value.forEach(row => {
      const thisNode = treeV2Dom.value.getNode(row[dataProps.value.value])
      if (thisNode) {
        if (show) {
          treeV2Dom.value.expandNode(thisNode)
        } else {
          treeV2Dom.value.collapseNode(thisNode)
        }
      }
    })
  } else {
    let showKey = nodeOrNodeKey
    // 如果是对象类型
    if (typeof nodeOrNodeKey == 'object') {
      showKey = nodeOrNodeKey[dataProps.value.value]
    }
    const thisNode = treeV2Dom.value.getNode(showKey)
    if (!thisNode) {
      return
    }
    if (show) {
      const fatherList = await findTreeLevelFn(showKey)
      treeV2Dom.value.expandNode(thisNode)
      // 展开父级
      fatherList?.forEach(row => {
        const fatherNode = treeV2Dom.value.getNode(row[dataProps.value.value])
        fatherNode && treeV2Dom.value.expandNode(fatherNode)
      })
    } else {
      treeV2Dom.value.collapseNode(thisNode)
    }
  }
}


// endregion


// region 过滤树形数据
const toFilter = (newV) => {
  treeV2Dom.value.filter && treeV2Dom.value.filter(newV)
}

// 进行过滤
watch(() => props.search, (newV, oldV) => {
  toFilter(newV)
}, {immediate: true, deep: true})

// 过滤方法
const filterFn = computed(() => {
  if (props.filterFn == undefined) {
    return (value, data) => {
      if (value == undefined || value.length <= 0) {
        return true
      }
      if (data[dataProps.value.label].includes(value)) {
        return true
      }
      return false
    }
  } else {
    return props.filterFn
  }
})

// endregion


// region 节点被点击

// 当前选择的节点
const nowNodeKey = ref("")
watch(() => props.modelValue, (newV, oldV) => {
  nowNodeKey.value = newV[dataProps.value.value]
}, {immediate: true, deep: true})


// 节点被点击
const nodeClickFn = (data, node, e) => {
  emit('nodeClick', {data, node, e})
  emit('update:modelValue', data)
}
// endregion


// region 滚动条样式

const scrollWidthX = ref('')
const scrollWidthY = ref('')
const scrollTranslateX = ref('')
const scrollTranslateY = ref('')
const scrollColorX = ref('')
const scrollColorY = ref('')
const scrollDisplayX = ref('')
const scrollDisplayY = ref('')
const scrollOpacityX = ref(0.7)
const scrollOpacityY = ref(0.7)
const scrollAlways = ref(false)

watch(() => props.scrollStyle, (newV, oldV) => {

  // region 宽度
  let width = newV.width == undefined ? '10px' : newV.width;
  scrollWidthX.value = getPx(width)
  scrollWidthY.value = getPx(width)
  if (newV.widthX != undefined) {
    scrollWidthX.value = getPx(newV.widthX)
  }
  if (newV.widthY != undefined) {
    scrollWidthY.value = getPx(newV.widthY)
  }
  // endregion

  // region 偏移量
  let translate = newV.width == undefined ? '3px' : newV.translate;
  scrollTranslateX.value = getPx(translate)
  scrollTranslateY.value = getPx(translate)
  if (newV.translateX != undefined) {
    scrollTranslateX.value = getPx(newV.translateX)
  }
  if (newV.translateY != undefined) {
    scrollTranslateY.value = getPx(newV.translateY)
  }

  // endregion

  // region 颜色
  let color = newV.color == undefined ? '#909399' : newV.color
  scrollColorX.value = color
  scrollColorY.value = color
  if (newV.colorX != undefined) {
    scrollColorX.value = newV.colorX
  }
  if (newV.colorY != undefined) {
    scrollColorY.value = newV.colorY
  }
  // endregion

  // region 透明度
  let opacity = newV.opacity == undefined ? 0.7 : newV.opacity
  scrollOpacityX.value = opacity
  scrollOpacityY.value = opacity
  if (newV.opacityX != undefined) {
    scrollOpacityX.value = newV.opacityX
  }
  if (newV.opacityY != undefined) {
    scrollOpacityY.value = newV.opacityY
  }
  // endregion

  // region 显示
  let show = (newV.show == undefined || newV.show == true) ? 'block' : 'none'
  scrollDisplayX.value = show
  scrollDisplayY.value = show
  if (newV.showX != undefined) {
    scrollDisplayX.value = (newV.showX == undefined || newV.showX == true) ? 'block' : 'none'
  }
  if (newV.showY != undefined) {
    scrollDisplayY.value = (newV.showY == undefined || newV.showY == true) ? 'block' : 'none'
  }
  // endregion

  // region 总是显示
  scrollAlways.value = (newV.always == undefined || newV.always == true) ? true : newV.always
  // endregion

}, {immediate: true, deep: true})

// endregion

// region 折叠/展开

const setExpand = (list = [], isShow = true) => {
  if (list == undefined || list.length <= 0) {
    // 处理全部
    console.log(234, levelList.value)

  } else {

  }
}

// endregion


onMounted(() => {
  getHW()
  window.addEventListener('resize', getHW)
})

onUnmounted(() => {
  window.removeEventListener('resize', getHW)
})

defineExpose({
  treeV2Dom: treeV2Dom,
  expandNode: expandNode,
})

</script>

<style scoped lang='scss'>
.xhykTreeV2 {
  //width: 100%;
  height: 100%;
}

.treeV2X {
  //width: 100%;

  // 溢出部分也显示
  .el-tree {
    overflow-x: scroll;
    width: 100% !important;
  }

  :deep(.el-tree-node) {
    width: fit-content !important;
    min-width: v-bind(fatherW) !important;
  }
}


// region 滚动条样式
:deep(.treeV2X) {

  // region 横向滚动条
  .is-horizontal {
    bottom: v-bind(scrollWidthY) !important;
    transform: translateY(v-bind(scrollTranslateY));

    .el-scrollbar__thumb {
      height: v-bind(scrollWidthY) !important;
      background-color: v-bind(scrollColorY);
      opacity: v-bind(scrollOpacityY);
      display: v-bind(scrollDisplayY);
    }
  }

  // endregion

  // region 纵向滚动条
  .el-virtual-scrollbar {
    right: v-bind(scrollWidthX) !important;
    transform: translateX(v-bind(scrollTranslateY));

    .el-scrollbar__thumb {
      width: v-bind(scrollWidthX) !important;
      background-color: v-bind(scrollColorX);
      opacity: v-bind(scrollOpacityX);
      display: v-bind(scrollDisplayX);
    }
  }

  // endregion

}

// endregion
</style>